import { ipcMain, BrowserWindow, IpcMainEvent } from 'electron'

// 定义监听器函数类型，接收 payload 和对应的窗口实例
type IpcHandler = (payload: any, win: BrowserWindow) => void

// 存储所有频道的监听器数组，key 为频道名，value 为监听函数数组
const listeners = new Map<string, IpcHandler[]>()

/**
 * 注册事件监听，支持同一频道多次监听
 * @param channel - 通信频道名称
 * @param callback - 监听回调，接收消息数据和触发事件的窗口
 */
function on(channel: string, callback: IpcHandler): void {
  // 首次注册该频道时，绑定 Electron ipcMain 事件转发到所有监听器
  if (!listeners.has(channel)) {
    listeners.set(channel, [])

    // 绑定 Electron 的 ipcMain 事件
    ipcMain.on(channel, (event: IpcMainEvent, payload: any) => {
      // 获取触发事件的窗口实例
      const win = BrowserWindow.fromWebContents(event.sender)
      if (!win) return // 无窗口则跳过

      // 逐个调用该频道所有注册的回调函数
      listeners.get(channel)?.forEach((fn) => fn(payload, win))
    })
  }

  // 将回调函数加入监听器列表
  listeners.get(channel)?.push(callback)
}

/**
 * 只监听一次事件，事件触发后自动取消监听
 * @param channel - 通信频道名称
 * @param callback - 监听回调
 */
function once(channel: string, callback: IpcHandler): void {
  // 包装回调，调用后取消注册
  const wrapper: IpcHandler = (payload, win) => {
    callback(payload, win)
    off(channel, wrapper) // 取消监听
  }
  on(channel, wrapper) // 注册包装后的回调
}

/**
 * 移除事件监听
 * @param channel - 通信频道名称
 * @param callback - 要移除的回调函数
 */
function off(channel: string, callback: IpcHandler): void {
  const list = listeners.get(channel)
  if (!list) return

  // 过滤掉指定的回调函数
  const filtered = list.filter((fn) => fn !== callback)

  // 如果还有监听函数，更新列表，否则删除频道
  if (filtered.length > 0) {
    listeners.set(channel, filtered)
  } else {
    listeners.delete(channel)
  }
}

/**
 * 向指定窗口发送消息
 * @param winName - 目标窗口的名称（title 或自定义 name）
 * @param channel - 频道名
 * @param payload - 发送的数据
 * @returns 是否成功发送
 */
function sendToWindow(winName: string, channel: string, payload: any): boolean {
  // 在所有窗口中查找符合名称的窗口
  const win = BrowserWindow.getAllWindows().find(
    (w) => w.getTitle() === winName || (w as any).name === winName
  )

  // 如果找到窗口且窗口可用，发送消息
  if (win && win.webContents) {
    win.webContents.send(channel, payload)
    return true
  }

  // 未找到目标窗口
  return false
}

export default { on, once, off, sendToWindow }
